package filefunc

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/gogf/gf/errors/gerror"
	websocket2 "github.com/gorilla/websocket"
	"github.com/spf13/cast"
	"gitlab.local/DO-module/new-filemanage-module/src/common"
	"gitlab.local/DO-module/new-filemanage-module/src/constant"
	"gitlab.local/DO-module/new-filemanage-module/src/controller/errcode"
	"gitlab.local/TerraMaster/tos-modules/folder"
	"mime/multipart"
	"net/url"
	"sync"

	//"gitlab.local/DO-module/new-filemanage-module/src/core/fileoperate"
	"gitlab.local/DO-module/new-filemanage-module/src/core/sqlitedb/sqlitemodel"
	"gitlab.local/DO-module/new-filemanage-module/src/service/file_recently"
	"gitlab.local/DO-module/new-filemanage-module/src/service/file_safeBox"
	"gitlab.local/DO-module/new-filemanage-module/src/service/filesystem/filenavigation"
	flog "gitlab.local/DO-module/new-filemanage-module/src/service/log"
	"gitlab.local/DO-module/new-filemanage-module/src/service/websocket"
	"gitlab.local/TerraMaster/tos-modules/fileoperate"
	"gitlab.local/golibrary/user"
	"gitlab.local/golibrary/utils"
	"gorm.io/gorm"
	"io"
	"log"
	"math"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"time"
)

func (Self *iosHttpFile) Filename() string {
	return Self.fileName
}

func (Self *iosHttpFile) Open() (io.ReadCloser, error) {
	return Self.r, nil
}

type multipartHttpFile struct {
	h *multipart.FileHeader
}

func (Self *multipartHttpFile) Filename() string {
	return Self.h.Filename
}

func (Self *multipartHttpFile) Open() (io.ReadCloser, error) {
	return Self.h.Open()
}

func (f *FileFunc) MethodType(mType string) websocket.HandlerFunc {
	handlers := map[string]websocket.HandlerFunc{
		"ping":                  f.pong,
		"fileUpload":            f.HandleFileUpload,
		"getBackgroundTaskLogs": f.HandleGetBackgroundTaskLogs,
	}
	// 根据type字段在映射表中查找对应的处理函数
	if handler, ok := handlers[mType]; ok {
		return handler
	}

	return nil
}

func (f *FileFunc) pong(conn *websocket2.Conn, p websocket.Params) (interface{}, error) {
	Pong := struct {
		Action string      `json:"action"`
		Data   interface{} `json:"data"`
	}{
		"pong",
		nil,
	}
	f.sendTaskData(conn, Pong)

	return Pong, nil
}

// ZipCompressFilter  zip压缩文件(过滤)
func (f *FileFunc) ZipCompressFilter(paths []string, outPath, uname string) (int64, error) {
	//权限判断
	if !f.aclService.IsWrite(outPath, uname) {
		return 0, gerror.NewCode(errcode.PermissionDenied)
	}
	for _, v := range paths {
		if !f.aclService.IsRead(v, uname) {
			return 0, gerror.NewCode(errcode.PermissionDenied)
		}
	}
	conf, _ := f.Recently.GetRecentlyConf(uname)
	coding := ""
	if conf != nil {
		coding = conf.Coding
	}
	taskParams := fileoperate.TaskParams{}
	taskParams.Uname = uname
	taskParams.TType = fileoperate.Compression
	taskParams.ToPath = outPath
	taskParams.Paths = paths
	taskParams.Coding = coding
	task, err := f.fOptGetter.RunFileOperationTask(taskParams)
	if err != nil {
		return 0, gerror.Wrap(err, "ZipCompressFilter")
	}

	return task, nil
}

// FileDownload 文件下载
func (f *FileFunc) FileDownload(uname string, files []string, body *gin.Context) error {
	for _, v := range files {
		if !f.aclService.IsRead(v, uname) {
			return errors.New(fmt.Sprintf(`[%s] no access`, v))
		}
	}
	conf, _ := f.Recently.GetRecentlyConf(uname)
	coding := ""
	if conf != nil {
		coding = conf.Coding
	}
	return utils.DownloadFile(body, files, coding)
}

// DelFolders 删除文件
func (f *FileFunc) DelFolders(username string, paths ...string) error {
	var baseFile string
	if len(paths) == 1 {
		baseFile = paths[0]
	}
	paths, err := f.DelFoldersWhiteList(paths) //白名单过滤
	if err != nil {
		return gerror.Wrap(err, "DelFolders11")
	}
	compile2, err := regexp.Compile(`^/Volume\d+/(@usb/usbshare\d+|@iso|@iscsi|@eSATA|[^/]+)/#recycle/.+`) //匹配回收站下面的文件
	if err != nil {
		return gerror.Wrap(err, "DelFolders1")
	}
	compile, err := regexp.Compile(`^/Volume\d+/(@usb/usbshare\d+|@iso|@iscsi|@eSATA|[^/]+)/`) //匹配共享文件夹路径
	if err != nil {
		return gerror.Wrap(err, "DelFolders2")
	}
	if len(paths) == 0 {
		return gerror.NewCode(errcode.SafeModification, baseFile)
	}

	//保险箱特殊处理
	lookup, err := user.Lookup(username)
	if err != nil {
		return err
	}

	reg := regexp.MustCompile(fmt.Sprintf(`^%s/?`, fmt.Sprintf(file_safeBox.MountSafeBoxDir, lookup.HomeDir)))
	safeBoxSrc := ""
	for _, v := range paths {
		//判断文件权限
		if !f.aclService.IsWrite(v, username) {
			return gerror.NewCode(errcode.PermissionDenied, v)
		}

		//判断文件所在文件夹权限
		if !f.aclService.IsWrite(filepath.Dir(v), username) {
			return gerror.NewCode(errcode.PermissionDenied, filepath.Dir(v))
		}
		safeBoxSrc = reg.FindString(v)
		if safeBoxSrc != "" {
			//判断保险箱是否开启，开启的话不做处理，没有开启需要处理
			nav := filenavigation.FileNav{}
			if !nav.IsMount(safeBoxSrc) {
				_ = nav.SafeBoxUnLock(safeBoxSrc)
				defer nav.SafeBoxLock(safeBoxSrc)
			}
		}

		//判断回收站权限
		tmpFile := v
		if !strings.Contains(tmpFile, username) {
			count := strings.Count(tmpFile, "/")
			for i := 0; i < count-2; i++ {
				tmpFile = filepath.Dir(tmpFile)
			}
			recyclePath := filepath.Join(tmpFile, "#recycle")
			if _, err := os.Stat(recyclePath); err == nil {
				if !f.aclService.IsWrite(recyclePath, username) {
					return gerror.NewCode(errcode.PermissionDenied, recyclePath)
				}
			}
		}

		if compile2.MatchString(v) { //回收站下文件直接删除
			if err = f.fOptGetter.DelFolders(v); err != nil {
				return gerror.Wrap(err, "DelFolders3")
			}
			continue
		}
		dir := compile.FindString(v)
		if dir == "" { //不是共享文件夹下文件直接删除
			if err = f.fOptGetter.DelFolders(v); err != nil {
				return gerror.Wrap(err, "DelFolders4")
			}
			continue
		}
		isRecycle, err := f.IsRecycle(dir)
		if err != nil {
			continue
		}
		if isRecycle {
			p, err := utils.AutoFolderName(filepath.Join(dir, "#recycle", filepath.Base(v)))
			if err != nil {
				return gerror.Wrap(err, "DelFolders7")
			}
			//已开启回收站
			if _, err = utils.ShellExec(fmt.Sprintf(`mv %s %s`, utils.StrSpecialLetters(v), utils.StrSpecialLetters(p))); err != nil {
				return gerror.Wrap(err, "DelFolders6")
			}
			continue
		}
		//没开启回收站直接删除
		if err = f.fOptGetter.DelFolders(v); err != nil {
			return gerror.Wrap(err, "DelFolders5")
		}
	}

	return nil
}

func (f *FileFunc) RemoteDownload(urlPath, outDir, uname string) error {
	//http协议下载
	if !strings.HasPrefix(urlPath, "https://") && !strings.HasPrefix(urlPath, "http://") {
		return errors.New("the download address is not supported")
	}
	//首先检查容量是否足够
	header, err := http.Head(urlPath)
	if err != nil {
		return err
	}
	if err = f.VerifyQuota(header.ContentLength, uname, outDir); err != nil {
		return err
	}
	if !f.aclService.IsWrite(outDir, uname) {
		return gerror.NewCode(errcode.PermissionDenied)
	}
	return f.newDownloadTask(urlPath, outDir, uname)
}

// UnZipCompress zip解压文件
// path 压缩文件路径
// outPath 解压输出文件夹路径
func (f *FileFunc) UnZipCompress(path, outPath, uname string) (int64, error) {

	if !f.aclService.IsRead(path, uname) {
		return 0, gerror.NewCode(errcode.PermissionDenied)
	}
	if !f.aclService.IsWrite(outPath, uname) {
		return 0, gerror.NewCode(errcode.PermissionDenied)
	}
	stat, err := os.Stat(outPath)
	if err != nil {
		return 0, gerror.Wrap(err, "UnZipCompress")
	}
	if !stat.IsDir() {
		return 0, errors.New("the output directory is not a folder")
	}

	//先检查容量能否满足解压后的文件大小。
	var size int64
	if err = f.fOptGetter.ZipActualSize(path, nil, &size, nil); err != nil {
		return 0, gerror.Wrap(err, "Calc size fail")
	}
	if err = f.VerifyQuota(size, uname, outPath); err != nil {
		return 0, gerror.NewCode(errcode.InsufficientHardDiskCapacity)
	}
	taskParams := fileoperate.TaskParams{}
	taskParams.Uname = uname
	taskParams.TType = fileoperate.Decompress
	taskParams.UnType = fileoperate.Zip
	taskParams.ToPath = outPath
	taskParams.Paths = []string{path}
	task, err := f.fOptGetter.RunFileOperationTask(taskParams)
	if err != nil {
		return 0, gerror.Wrap(err, "UnZipCompress2")
	}

	return task, nil
}

// UnrarCompress rar解压文件
// p 压缩文件路径
// outPath 解压输出文件夹路径
func (f *FileFunc) UnrarCompress(path, outPath, uname string) (int64, error) {
	if !f.aclService.IsRead(path, uname) {
		return 0, gerror.NewCode(errcode.PermissionDenied)
	}
	if !f.aclService.IsWrite(outPath, uname) {
		return 0, gerror.NewCode(errcode.PermissionDenied, outPath)
	}
	stat, err := os.Stat(outPath)
	if err != nil {
		return 0, gerror.Wrap(err, "UnRarCompress")
	}
	if !stat.IsDir() {
		return 0, errors.New("the output directory is not a folder")
	}

	//先检查容量能否满足解压后的文件大小。
	res, err := utils.ShellExec(fmt.Sprintf("unrar l '%s' | awk '/./{line=$0} END{print line}' | awk '{print $1}'", path))
	if err = f.VerifyQuota(cast.ToInt64(strings.TrimSpace(res)), uname, outPath); err != nil {
		return 0, err
	}
	taskParams := fileoperate.TaskParams{}
	taskParams.Uname = uname
	taskParams.TType = fileoperate.Decompress
	taskParams.ToPath = outPath
	taskParams.Size = cast.ToInt64(strings.TrimSpace(res))
	taskParams.UnType = fileoperate.Rar
	taskParams.Paths = []string{path}
	task, err := f.fOptGetter.RunFileOperationTask(taskParams)
	if err != nil {
		return 0, gerror.Wrap(err, "UnRarCompress err")
	}
	return task, nil
}

// DelFoldersWhiteList 文件夹删除白名单
func (f *FileFunc) DelFoldersWhiteList(paths []string) ([]string, error) {
	data := make([]string, 0)
	compile1 := regexp.MustCompile(`^/Volume\d+/(@usb/usbshare\d+|@iso|@iscsi|@eSATA|[^/]+)/#recycle/?$`) //不能删除回收站
	//匹配保险箱路径
	for _, v := range paths {
		if common.SafeBoxRegex.MatchString(v) {
			//不能删除保险箱
			continue
		}
		if compile1.MatchString(v) { //不能删除回收站
			continue
		}
		data = append(data, v)
	}
	return data, nil
}

// IsRecycle 共享文件夹是否开启回收站功能 param: mntpath 共享文件夹绝对路径
func (f *FileFunc) IsRecycle(mntpath string) (bool, error) {
	//删除末尾多余斜杠
	var res sqlitemodel.Share
	err := f.db.DbGet().Where("mntpath=?", strings.TrimRight(mntpath, "/")).First(&res).Error
	if err == gorm.ErrRecordNotFound { //无记入
		return false, nil
	}
	if err != nil {
		return false, gerror.Wrap(err, "IsRecycle")
	}
	return res.Recycle != 0, nil
}

// FileExist 文件是否存在
func (f *FileFunc) FileExist(data FileData) bool {
	if !utils.Exists(data.Path) { //判断文件是否存在
		//文件不存在
		return false
	}
	stat, err := os.Stat(data.Path)
	if err != nil {
		return false
	}

	if stat.IsDir() { //文件夹
		return true
	}

	if data.FileType != "ios" {
		if data.Size != stat.Size() {
			return false
		}
	}
	if data.LastTime != stat.ModTime().Unix() { //判断文件大小和修改时间是否一致
		//不一致
		return false
	}

	if data.Md5 != "" && data.Md5 != utils.Gethashfile(data.Path) { //对比md5
		//md5不一致
		return false
	}

	return true
}

// VerifyQuota 验证用户的配额   文件大小，用户，目标路径
func (f *FileFunc) VerifyQuota(size int64, username, path string) error {
	var (
		overUserQuota   = gerror.New("over user quota")
		overFolderQuota = gerror.New("over folder quota")
		overVolumeQuota = gerror.New("over volume capacity")
	)
	s := strings.Split(path, "/")
	if len(s) < 2 {
		return gerror.New(fmt.Sprintf("invalid param %s", path))
	}
	volume := s[1]
	if volume == "home" {
		cmd := fmt.Sprintf(`df-json | egrep '(%s)$'| awk '{print $1}' | awk 'END{print}'`, volume)
		result, _ := utils.ShellExec(cmd)
		//result, _ := utils.ShellUserExec("abc", cmd)

		device := strings.TrimSpace(result)
		log.Println("device", device)
		cmd = fmt.Sprintf(`df-json | grep '%s' | egrep '(Volume[0-9]+)$' | awk '{print $8}'`, device)
		r, _ := utils.ShellExec(cmd)
		volume = strings.TrimLeft(strings.TrimSpace(r), "/")
	}

	//step1 验证用户ext4配额
	//在测试的时候发现quota -u '%s' --format=vfsv1 -w --hide-device --show-mntpoint -l | grep '%s' | awk '{print $2,$4}' 这个命令无效要加上v
	cmd := fmt.Sprintf(`quota -uv '%s' --format=vfsv1 -w --hide-device --show-mntpoint -l | grep '%s' | awk '{print $2,$4}'`, username, volume)
	if result, _ := utils.ShellExec(cmd); result != "" {
		//包括*表示已经超过配额了
		if strings.Contains(result, "*") {
			return overUserQuota
		}
		result = strings.TrimSpace(result)
		r := strings.Split(result, " ")
		if len(r) < 2 {
			return overUserQuota
		}
		//没有配额，capacity的容量为0
		capacity := cast.ToInt64(r[1]) * 1024
		if capacity != 0 {
			used := cast.ToInt64(r[0]) * 1024
			if used+size >= capacity {
				return overUserQuota
			}
		}

	}
	//step2 btrfs 文件限额
	used, limit, b := utils.BtrfsShareUserQuota(path)
	if limit != 0 {
		if b {
			return overFolderQuota
		}
		if used+size >= limit {
			return overFolderQuota
		}
	}
	//step文件系统的限制
	cmd = fmt.Sprintf(`df-json | egrep "(%s)$" |awk '{print $5}'`, volume)
	res, _ := utils.ShellExec(cmd)
	avail := cast.ToInt64(strings.TrimSpace(res)) * 1024
	if size >= avail {
		return overVolumeQuota
	}

	return nil

}

// FileCompress 压缩
func (f *FileFunc) FileCompress(c *gin.Context, uname string) (interface{}, error) {
	var data struct {
		Paths   []string `json:"paths"`    //被压缩文件路径
		OutPath string   `json:"out_path"` //压缩输出路径(文件夹)
	}
	//解析json
	if e := c.BindJSON(&data); e != nil {
		flog.RecordedLog(flog.Err, uname, flog.HttpTag, "failed to compress files", c.ClientIP())
		return nil, e
	}

	if len(data.Paths) == 0 || data.OutPath == "" {
		flog.RecordedLog(flog.Err, uname, flog.HttpTag, "failed to compress files", c.ClientIP())
		return "", errors.New("parameter parsing failed")
	}
	taskId, e := f.ZipCompressFilter(data.Paths, data.OutPath, uname)
	if e != nil {
		flog.RecordedLog(flog.Err, uname, flog.HttpTag, "failed to compress files", c.ClientIP())
		return "", e

	}
	flog.RecordedLog(flog.Info, uname, flog.HttpTag, "compressed file successfully", c.ClientIP())

	if taskId == 0 {
		return "", nil
	}

	return strconv.FormatInt(taskId, 10), nil
}

// UnCompress 解压
func (f *FileFunc) UnCompress(c *gin.Context, uname string) (interface{}, error) {
	var data struct {
		Path    string `json:"path"`     //压缩文件路径
		OutPath string `json:"out_path"` //解压输出路径(文件夹)
	}
	//解析json
	if e := c.BindJSON(&data); e != nil {
		flog.RecordedLog(flog.Err, uname, flog.HttpTag, "failed to decompress the file", c.ClientIP())
		return "", e
	}
	if data.Path == "" || data.OutPath == "" {
		flog.RecordedLog(flog.Err, uname, flog.HttpTag, "failed to decompress the file", c.ClientIP())
		return "", errors.New("parameter parsing failed")
	}
	// 直接用文件后缀判断是否能够解压
	pathExt := filepath.Ext(data.Path)
	var taskId int64
	var err error
	switch pathExt {
	case ".zip":
		taskId, err = f.UnZipCompress(data.Path, data.OutPath, uname)
		if err != nil {
			flog.RecordedLog(flog.Err, uname, flog.HttpTag, "failed to decompress the file", c.ClientIP())
			return "", err
		}
	case ".rar":
		if taskId, err = f.UnrarCompress(data.Path, data.OutPath, uname); err != nil {
			flog.RecordedLog(flog.Err, uname, flog.HttpTag, "failed to decompress the file", c.ClientIP())
			return "", err
		}
	default:
		flog.RecordedLog(flog.Err, uname, flog.HttpTag, "failed to decompress the file", c.ClientIP())
		return "", errors.New("parameter parsing failed")
	}
	flog.RecordedLog(flog.Info, uname, flog.HttpTag, "decompressed file successfully", c.ClientIP())

	if taskId == 0 {
		return "", nil
	}
	return strconv.FormatInt(taskId, 10), nil
}

// MoveCopyFiles 移动复制
func (f *FileFunc) MoveCopyFiles(c *gin.Context, uname string) (interface{}, error) {
	var data struct {
		Paths  []string                  `json:"paths"`   //文件路径
		ToPath string                    `json:"to_path"` //目标文件夹
		Type   fileoperate.FileOperation `json:"type"`    //1.复制 2.移动
	}
	//解析json
	if err := c.BindJSON(&data); err != nil {
		flog.RecordedLog(flog.Err, uname, flog.HttpTag, "failed to move or copy a file or folder", c.ClientIP())
		return "", err
	}
	if len(data.Paths) == 0 || data.ToPath == "" {
		flog.RecordedLog(flog.Err, uname, flog.HttpTag, "failed to move or copy a file or folder", c.ClientIP())
		return "", errors.New("parameter parsing failed")
	}
	//白名单过滤
	if data.Type == fileoperate.Move {
		if len(data.Paths) == 1 {
			newPaths, _ := f.DelFoldersWhiteList(data.Paths)
			if len(newPaths) == 0 {
				return nil, gerror.NewCode(errcode.SafeModification, data.Paths[0])
			}
		}
	}

	tid, err := f.fOptGetter.MoveFiles(uname, data.Paths, data.ToPath, data.Type, false)
	if err != nil {
		flog.RecordedLog(flog.Err, uname, flog.HttpTag, "failed to move or copy a file or folder", c.ClientIP())
		return tid, err
	}
	flog.RecordedLog(flog.Info, uname, flog.HttpTag, "succeeded in moving or copying a file or folder procedure", c.ClientIP())
	return strconv.FormatInt(tid, 10), nil //转int为string，防止js精度丢失
}

// PreHandleRemoteDownload 预处理远程文件下载
func (f *FileFunc) PreHandleRemoteDownload(c *gin.Context, uname string) (interface{}, error) {
	var data struct {
		Src    string `json:"src"`     //下载地址
		OutDir string `json:"out_dir"` //保存目录
	}

	//解析json
	if e := c.BindJSON(&data); e != nil {
		flog.RecordedLog(flog.Err, uname, flog.HttpTag, "remote download failure", c.ClientIP())
		return false, e
	}
	data.Src = strings.TrimSpace(data.Src)
	data.OutDir = strings.TrimSpace(data.OutDir)
	if data.Src == "" || data.OutDir == "" {
		flog.RecordedLog(flog.Err, uname, flog.HttpTag, "remote download failure", c.ClientIP())
		return false, errors.New("parameter parsing failed")
	}

	if e := f.RemoteDownload(data.Src, data.OutDir, uname); e != nil {
		flog.RecordedLog(flog.Err, uname, flog.HttpTag, "remote download failure", c.ClientIP())
		return false, e
	}

	return true, nil
}

//----------------------websocket-----------------

// HandleFileUpload 分片上传文件
func (f *FileFunc) HandleFileUpload(conn *websocket2.Conn, p websocket.Params) (interface{}, error) {
	data := Data{}
	err := json.Unmarshal(p.Data, &data)
	if err != nil {
		// 处理反序列化错误
		return "", err
	}
	if data.Size > 0 {
		//权限判断
		if !f.aclService.IsWrite(data.Path, p.Name) {
			return nil, gerror.NewCode(errcode.PermissionDenied, data.Path)
		}
		_, err = f.uploadTask(data, p.Name)
	} else {
		err = errors.New("size err")
	}

	if err != nil {
		f.sendTaskData(conn, err)
	}
	return nil, nil
}

// HandleUnCompress 文件解压
func (f *FileFunc) HandleUnCompress(_ *websocket2.Conn, p websocket.Params) (interface{}, error) {

	var data struct {
		Path    string `json:"path"`     //压缩文件路径
		OutPath string `json:"out_path"` //解压
		// 输出路径(文件夹)
	}
	//解析json
	err := json.Unmarshal(p.Data, &data)
	if err != nil {
		flog.RecordedLog(flog.Err, p.Name, flog.HttpTag, "remote download failure", p.ClientIP)
		return "", err
	}

	if data.Path == "" || data.OutPath == "" {
		flog.RecordedLog(flog.Err, p.Name, flog.HttpTag, "failed to decompress the file", p.ClientIP)
		return "", errors.New("parameter parsing failed")
	}
	// 直接用文件后缀判断是否能够解压
	pathExt := filepath.Ext(data.Path)
	var taskId int64
	switch pathExt {
	case ".zip":
		taskId, err = f.UnZipCompress(data.Path, data.OutPath, p.Name)
		if err != nil {
			flog.RecordedLog(flog.Err, p.Name, flog.HttpTag, "failed to decompress the file", p.ClientIP)
			return "", err
		}
	case ".rar":
		if taskId, err = f.UnrarCompress(data.Path, data.OutPath, p.Name); err != nil {
			flog.RecordedLog(flog.Err, p.Name, flog.HttpTag, "failed to decompress the file", p.ClientIP)
			return "", err
		}
	default:
		flog.RecordedLog(flog.Err, p.Name, flog.HttpTag, "failed to decompress the file", p.ClientIP)
		return "", errors.New("parameter parsing failed")
	}
	flog.RecordedLog(flog.Info, p.Name, flog.HttpTag, "decompressed file successfully", p.ClientIP)

	if taskId == 0 {
		return "", nil
	}

	return strconv.FormatInt(taskId, 10), nil
}

// HandleGetBackgroundTaskLogs 获取后台任务日志
func (f *FileFunc) HandleGetBackgroundTaskLogs(conn *websocket2.Conn, p websocket.Params) (interface{}, error) {
	stop := make(chan struct{}) // 创建一个停止通道
	go f.allTask(conn, p.Name, stop)

	return "", nil
}

// allTask 任务
func (f *FileFunc) allTask(conn *websocket2.Conn, uname string, stop <-chan struct{}) {
	end := 1
	for {
		select {
		case <-stop:
			return
		default:
			data, err := f.getTaskData(uname)
			if err != nil {
				log.Println("Error retrieving task data:", err)
				continue
			}
			// 任务完成推送结束符
			if len(data) == 0 {
				end++
			} else {
				end = 0
			}
			if end <= 1 {
				if end == 1 {
					data = make([]AllBackgroundTaskLog, 0)
				}
				f.sendTaskData(conn, data)
			}

			time.Sleep(1 * time.Second)
		}
	}
}

func (f *FileFunc) getTaskData(uname string) ([]AllBackgroundTaskLog, error) {
	var data []AllBackgroundTaskLog
	q, b := f.remoteDownloadQueue[uname]

	if b {
		for _, v := range q.queue {
			rate, err := v.Rate()
			if err != nil {
				return nil, err
			}
			currentSize := v.Size
			if rate != 100.00 {
				currentSize = int64(math.Ceil(float64(v.Size) * float64(rate) / 100))
			}
			data = append(data, AllBackgroundTaskLog{
				TaskId:      v.TaskId,
				Type:        6,
				Name:        filepath.Base(v.OutFile),
				Size:        v.Size,
				Rate:        rate,
				Status:      v.Status,
				ToPath:      v.OutFile,
				CurrentSize: currentSize,
				TaskType:    "download",
			})
		}
	}

	task, err := f.fOptGetter.GetBackgroundTaskLogs(uname)
	if err != nil {
		return nil, err
	}

	for _, t := range task {
		tl := AllBackgroundTaskLog{
			TaskId:      t.TaskId,
			Type:        t.Type,
			Name:        t.Name,
			Rate:        t.Rate,
			Size:        t.Size,
			ErrorMsg:    t.ErrorMsg,
			Status:      t.Status,
			FileTotal:   t.FileTotal,
			CurrentSize: t.CurrentSize,
			ToPath:      t.ToPath,
			TaskType:    "other",
		}
		data = append(data, tl)
	}

	return data, nil
}

var mu sync.Mutex // Create a mutex for protecting the WebSocket connection

func (f *FileFunc) sendTaskData(conn *websocket2.Conn, data interface{}) {
	mu.Lock()
	defer mu.Unlock()
	jsonBytes, err := json.Marshal(data)
	if err != nil {
		log.Println("JSON Marshal error:", err)
		return
	}
	err = conn.WriteMessage(websocket2.TextMessage, jsonBytes)
	if err != nil {
		log.Println("Push process fail:", err)
		return
	}
}

// HandleGetRemoteDownloadList 获取远程下载任务
func (f *FileFunc) HandleGetRemoteDownloadList(userName string) ([]folder.DownloadLog, error) {
	data := make([]folder.DownloadLog, 0)

	q, b := f.remoteDownloadQueue[userName]
	if !b {
		return data, nil
	}

	for _, v := range q.queue {
		rate, _ := v.Rate()
		currentSize := v.Size
		if rate != 100.00 {
			currentSize = int64(math.Ceil(float64(v.Size) * float64(rate) / 100))
		}
		data = append(data, folder.DownloadLog{
			TaskId:      v.TaskId,
			Name:        filepath.Base(v.OutFile),
			Size:        v.Size,
			Rate:        rate,
			Status:      v.Status,
			ToPath:      v.OutFile,
			CurrentSize: currentSize,
		})
	}

	return data, nil
}

// uploadTask 文件上传任务
func (f *FileFunc) uploadTask(data Data, userName string) (interface{}, error) {
	// 创建文件
	uploadFilePath := filepath.Join(data.Path, data.FileName)
	filename := data.FileName
	exist := utils.Exists(uploadFilePath)
	if exist {
		//"" 没有初始化 1  重命名  2  覆盖 3  跳过
		if f.uploadStatus == "" {
			fileUploadStatusHandle(f, userName)
		}
		if data.BackUp == 1 {
			return f.fOptGetter.GetFolder(data.Path)
		}
		if f.uploadStatus == "1" {
			filePath, _ := utils.AutoFolderName(filepath.Join(data.Path, data.FileName))
			filename = filepath.Base(filePath)
		} else if f.uploadStatus == "2" {
			rmCmd := fmt.Sprintf(`rm -rf %s;`, uploadFilePath)
			if _, err := utils.ShellExec(rmCmd); err != nil {
				return nil, gerror.NewCode(errcode.FailedToDelete)
			}
		} else { // == "3"
			return nil, nil
		}
	}
	if data.Index == 1 {
		_, err := f.diskSpace(data)
		if err != nil {
			return nil, err
		}
	}
	//判断容量限制
	if data.Index == 1 && f.fOptGetter.VolumeUserQuota(data.Path, userName, data.Size) {
		return "", errors.New("drive space alert")
	}

	//临时文件前缀
	fileN := filepath.Base(filename)
	//临时文件路径
	fileName := filepath.Join(data.Path, constant.IgnoreFilePre+fileN)
	if data.Index == 1 { //重新开始，清除残留文件
		_ = os.Remove(filename)
	}

	if data.Index > 1 && !utils.Exists(fileName) { //当索引值大于1时，判断临时文件是否存在，不存在为上传中断，上传失败
		return "", errors.New("file upload error, previous section of file missing")
	}
	file, err := os.OpenFile(fileName, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660)
	if err != nil {
		return "", gerror.Wrap(err, "FileUploadConvention1")
	}
	defer file.Close()
	if data.Index == 1 {
		taskParams := fileoperate.TaskParams{}
		taskParams.Uname = userName
		taskParams.Size = data.Size
		taskParams.TType = fileoperate.Upload
		taskParams.ToPath = data.Path
		taskParams.FileTotal = uint8(data.FileTotal)
		taskParams.Paths = []string{uploadFilePath, fileName}
		_, e := f.fOptGetter.RunFileOperationTask(taskParams)
		if e != nil {
			return "", gerror.Wrap(e, "Upload")
		}
	}
	//文件删除计时器,一分钟后没请求则删除临时文件,有请求则重置计时器
	_ = f.reloadFileDelTimer(fileName, time.Minute, func() {
		_ = os.RemoveAll(fileName)
	})
	// 接收文件内容并写入文件
	reader := bytes.NewReader(data.ChunkData)
	if _, err = io.Copy(file, reader); err != nil {
		_ = os.Remove(fileName)
		return "", gerror.Wrap(err, "FileUploadConvention2")
	}

	if data.Index == data.TotalIndex {
		//上传成功停止计时器
		_ = f.stopFileDelTimer(fileName)
		stat, _ := file.Stat()
		//判断文件大小是否相等
		if stat.Size() != data.Size {
			_ = os.Remove(fileName)
			return "", errors.New("file sizes vary and files are missing")
		}
		newFileName, err := utils.AutoFolderName(filepath.Join(data.Path, fileN))
		if err != nil {
			_ = os.Remove(fileName)
			return "", gerror.Wrap(err, "FileUploadConvention4")
		}

		//重命名文件
		if err = os.Rename(fileName, newFileName); err != nil {
			_ = os.Remove(fileName)
			return "", gerror.Wrap(err, "FileUploadConvention5")
		}
		//修改文件拥有者
		_ = f.aclService.Chown(newFileName, userName)
		////修改文件最后修改时间
		//_ = f.modifyLastModTime(newFileName, lastModTime)
		//继承父级acl权限
		_ = f.aclService.InheritedPermissions(newFileName, "", false)
	}

	return nil, nil
}

func (f *FileFunc) diskSpace(data Data) (interface{}, error) {
	//判断磁盘空间是否充足
	var s sqlitemodel.Share
	f.db.DbGet().Table("share").Where("mntpath = ?", data.Path).Select("quota_flag").Find(&s)
	var pathFreeSize uint64
	var err error
	//根据是否开启磁盘配额来查询目录的剩余空间
	if s.QuotaFlag == 1 {
		used, limit, b := utils.BtrfsShareUserQuota(data.Path)
		if b {
			return "", errors.New("Insufficient storage space")
		}
		free := limit - used
		pathFreeSize = uint64(free * 8)
	} else {
		pathFreeSize, err = utils.GetPathSize(data.Path)
		if err != nil {
			return "", errors.New("FileUpload1")
		}
	}

	if uint64(data.Size) > pathFreeSize {
		return "", gerror.NewCode(errcode.InsufficientHardDiskCapacity)
	}
	return nil, nil
}

func (f *FileFunc) HandleFileUploadOld(c *gin.Context, uname string) (interface{}, error) {
	var (
		fileName                 string
		dir                      string
		totalIndex, index, isMsg uint64
		size, lastModTime        int64
		e                        error
		autoDelete               bool
		totalSize                int64
		totalFile, fileIndex     uint64
		chunkSize                int64
	)

	//上传目录

	//权限判断
	//if !fc.folders.Acl.IsWrite(dir, uname) {
	//	return nil, gerror.NewCode(errcode.PermissionDenied, dir)
	//}

	var fileData folder.HttpFile

	if fileName = c.Request.Header.Get("Ios-File-Name"); fileName != "" {

		tmpData := &iosHttpFile{
			r: c.Request.Body,
		}

		dir, _ = url.QueryUnescape(c.Request.Header.Get("Tos-Path"))
		totalIndexInt, e := strconv.Atoi(c.Request.Header.Get("Ios-File-Part-Total-Index"))
		if e != nil {
			flog.RecordedLog(flog.Err, uname, flog.HttpTag, "uploading files failed procedure(3)", c.ClientIP())
			return false, e
		}
		if totalIndexInt == 0 {
			flog.RecordedLog(flog.Err, uname, flog.HttpTag, "uploading files failed procedure(4)", c.ClientIP())
			return false, errors.New("total_index as 0")
		}
		totalIndex = uint64(totalIndexInt)

		index, e = strconv.ParseUint(c.Request.Header.Get("Ios-File-Part-Index"), 10, 64)
		if e != nil {
			flog.RecordedLog(flog.Err, uname, flog.HttpTag, "uploading files failed procedure(5)", c.ClientIP())
			return false, e
		}

		size, e = strconv.ParseInt(c.Request.Header.Get("Ios-File-Size"), 10, 64)
		if e != nil {
			flog.RecordedLog(flog.Err, uname, flog.HttpTag, "uploading files failed procedure(6)", c.ClientIP())
			return false, e
		}

		lastModTime, _ = strconv.ParseInt(c.Request.Header.Get("Ios-Last-Mod-Time"), 10, 64)

		lastModTime, _ = strconv.ParseInt(c.Request.Header.Get("Ios-Last-Mod-Time"), 10, 64)

		autoDelete = false

		tmpName, _ := base64.StdEncoding.DecodeString(fileName)
		tmpData.fileName = string(tmpName)
		fileData = tmpData
	} else {
		file, _ := c.FormFile("file")
		//修改文件名称
		dir = c.PostForm("path")
		difFile := filepath.Join(dir, file.Filename)

		if utils.Exists(difFile) {
			if f.uploadStatus == "" {
				fileUploadStatusHandle(f, uname)
			}
			//"" 没有初始化
			//1  重命名
			//2  覆盖
			//3  跳过
			//uploadStatusHandle(fc, uname)
			if f.uploadStatus == "1" {
				filePath, _ := utils.AutoFolderName(filepath.Join(dir, file.Filename))
				file.Filename = filepath.Base(filePath)
			} else if f.uploadStatus == "2" {
				rmCmd := fmt.Sprintf(`rm -rf %s;`, difFile)
				if _, err := utils.ShellExec(rmCmd); err != nil {
					return nil, gerror.NewCode(errcode.FailedToDelete)
				}
			} else {
				// == "3"
				return nil, nil
			}
		}
		tmpData := &multipartHttpFile{h: file}
		chunkSize = file.Size
		//总分片数
		i := c.PostForm("total_index")
		totalIndex, e = strconv.ParseUint(i, 10, 64)
		if e != nil {
			flog.RecordedLog(flog.Err, uname, flog.HttpTag, "uploading files failed procedure(3)", c.ClientIP())
			return false, e
		}
		if totalIndex == 0 {
			flog.RecordedLog(flog.Err, uname, flog.HttpTag, "uploading files failed procedure(4)", c.ClientIP())
			return false, errors.New("total_index as 0")
		}
		//当前索引
		index, e = strconv.ParseUint(c.PostForm("index"), 10, 64)
		if e != nil {
			flog.RecordedLog(flog.Err, uname, flog.HttpTag, "uploading files failed procedure(5)", c.ClientIP())
			return false, e
		}
		//总文件大小
		size, e = strconv.ParseInt(c.PostForm("size"), 10, 64)
		if e != nil {
			flog.RecordedLog(flog.Err, uname, flog.HttpTag, "uploading files failed procedure(6)", c.ClientIP())
			return false, e
		}
		isMsg, e = strconv.ParseUint(c.PostForm("is_msg"), 10, 64)
		if e != nil {
			flog.RecordedLog(flog.Err, uname, flog.HttpTag, "uploading files failed procedure(7)", c.ClientIP())
			return false, e
		}
		if isMsg == 1 {
			//多文件上传总文件大小
			totalSize, e = strconv.ParseInt(c.PostForm("total_size"), 10, 64)
			if e != nil {
				flog.RecordedLog(flog.Err, uname, flog.HttpTag, "uploading files failed procedure(8)", c.ClientIP())
				return false, e
			}
			totalFile, e = strconv.ParseUint(c.PostForm("total_file"), 10, 64)
			if e != nil {
				flog.RecordedLog(flog.Err, uname, flog.HttpTag, "uploading files failed procedure(9)", c.ClientIP())
				return false, e
			}
			fileIndex, e = strconv.ParseUint(c.PostForm("file_index"), 10, 64)
			if e != nil {
				flog.RecordedLog(flog.Err, uname, flog.HttpTag, "uploading files failed procedure(9)", c.ClientIP())
				return false, e
			}
		}

		autoDelete = c.PostForm("auto_delete") == "1"
		//最后文件修改时间不处理错误容错零值
		lastModTime, _ = strconv.ParseInt(c.PostForm("last_mod_time"), 10, 64)
		fileData = tmpData
	}

	//---------------------------------------

	var res string
	//进任务队列
	if isMsg == 1 {
		res, e = f.FileUploadMsg(fileData,
			dir,
			totalIndex,
			index,
			isMsg,
			fileIndex,
			totalFile,
			autoDelete,
			size,
			totalSize,
			chunkSize,
			uname,
			lastModTime)
	} else {
		res, e = f.FileUpload(fileData, dir, totalIndex, index, autoDelete, size, uname, lastModTime)
	}

	if e != nil {
		flog.RecordedLog(flog.Err, uname, flog.HttpTag, "uploading files failed procedure(7)", c.ClientIP())
		return res, e
	}
	if index >= totalIndex {
		flog.RecordedLog(flog.Info, uname, flog.HttpTag, "uploading file succeeded", c.ClientIP())
		//记入最近
		_ = f.Recently.WriteRecently(uname, res)
		res = ""
	}

	return res, e
}

func fileUploadStatusHandle(f *FileFunc, uName string) {
	conf, _ := f.Recently.GetRecentlyConf(uName)
	switch conf.UploadFileOperation {
	case file_recently.Rename:
		f.uploadStatus = "1"
	case file_recently.Cover:
		f.uploadStatus = "2"
	case file_recently.Skip:
		f.uploadStatus = "3"
	default:

	}
}
